#include "key.h"
#include "objectlooper.h"

//default
#define KEY_DELAY_MS 20
#define KEY_DCLICK_MS 500
#define KEY_LONG_MS  1000

#define KEY_TICK_MS 5

//
static void KeyLoop(key_t *key);

void KeyInit(key_t *key, uint8_t id, SOC_GpioPort_t port, uint8_t pin)
{
    key->id = id;
    key->port = port;
    key->pin = pin;
    key->reverse = FALSE;

    key->downEvent = NULL;
    key->upEvent = NULL;
    key->dClickEvent = NULL;
    key->longDownEvent = NULL;

    key->state = KEY_IDLE;
    key->countMs = 0;   
    key->prePinVal = 0xff;
    key->hasLongDown = FALSE;
    key->lastClickMsDecrement = 0;

    SOC_GpioInit(port , pin , S_GPIO_Mode_IN_PULLUP); 
    key->isInit = TRUE;
    
    RegisterObjectLooper(key, KEY_TICK_MS, KeyLoop); 
}

void KeySetReverse(key_t *key, bool reverse)
{
   key->reverse = reverse;
}

static void KeyLoopImpl(key_t *key, int ms)
{
    int v;

    v = SOC_GpioRead(key->port , key->pin) != 0;
    if(key->reverse)
        v = !v;

    switch(key->state)
    {
    case KEY_IDLE:
        if(key->prePinVal != 0 && v == 0)
        {
            key->state = KEY_DLY;
            key->countMs = 0;
            key->hasLongDown = FALSE;
        }
    break;
    case KEY_DLY:
        if(key->countMs >= KEY_DELAY_MS)
        {
            if(v == 0)
            {
                if(key->downEvent != NULL)
                {
                    key->downEvent(key);
                }

                if(key->lastClickMsDecrement > 0 && key->dClickEvent != NULL)
                {
                    key->dClickEvent(key);
                }

                key->lastClickMsDecrement = KEY_DCLICK_MS;
                key->state = KEY_DOWN;
            }
            else 
            {
                key->state = KEY_IDLE;
            }
        }
    break;
    case KEY_DOWN:
        if(v == 1)
        {
            if(key->upEvent != NULL)
            {
                key->upEvent(key);
            }

            key->countMs = 0;
            key->state = KEY_UP;
        }
        else
        {
            if(!key->hasLongDown && key->countMs >= KEY_LONG_MS)
            {
                key->hasLongDown = TRUE;
                if(key->longDownEvent != NULL)
                {
                    key->longDownEvent(key);
                }
            }
        }
        break;
    case KEY_UP:
        if(key->countMs >= KEY_DELAY_MS)
        {
            key->state = KEY_IDLE;
        }
        break;
    }

    key->countMs += ms;
    key->prePinVal = v;
    //count down 
    if(key->lastClickMsDecrement > 0)
    {
        key->lastClickMsDecrement -= ms;
        if(key->lastClickMsDecrement < 0)
        {
            key->lastClickMsDecrement = 0;
        }
    }
}

static void KeyLoop(key_t *key)
{
    if(!key->isInit)
        return;
    
    KeyLoopImpl(key, KEY_TICK_MS);
}

void KeySetDownCallBack(key_t *key, KEY_CALLBACK cb)
{
    key->downEvent = cb;
}

void KeySetUpCallBack(key_t *key, KEY_CALLBACK cb)
{
    key->upEvent = cb;
}

void KeySetDClickCallBack(key_t *key, KEY_CALLBACK cb)
{
    key->dClickEvent = cb;
}

void KeySetLongDownCallBack(key_t *key, KEY_CALLBACK cb)
{
    key->longDownEvent = cb;
}
